using System;
using System.Windows;
using System.Windows.Media;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Supremacy.Universe;

using Color = Microsoft.Xna.Framework.Graphics.Color;
using Matrix = Microsoft.Xna.Framework.Matrix;

namespace Supremacy.Xna
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public class SunView3D : XnaComponent
    {
        // ReSharper disable InconsistentNaming
        private Matrix _projectionMatrix;
        private RenderTarget2D _tempBuffer;
        private float _dt;
        private PostProcessor _postProcessor;
        private bool _usePostProcessor;
        private Sun _sun;
        private EllipseGeometry _clip;
        // ReSharper restore InconsistentNaming

        #region StarType Property
        public static readonly DependencyProperty StarTypeProperty = DependencyProperty.Register(
            "StarType",
            typeof(StarType),
            typeof(SunView3D),
            new PropertyMetadata(Supremacy.Universe.StarType.Yellow));

        public SunView3D()
            : base(false, false, false)
        {
            this.Width = 128;
            this.Height = 128;
            
            _clip = new EllipseGeometry(new Rect(0, 0, 128, 128));
        }

        public StarType StarType
        {
            get { return (StarType)GetValue(StarTypeProperty); }
            set { SetValue(StarTypeProperty, value); }
        }
        #endregion

        #region CameraDistanceMultiplier Property
        public static readonly DependencyProperty CameraDistanceMultiplierProperty = DependencyProperty.Register(
            "CameraDistanceMultiplier",
            typeof(float),
            typeof(SunView3D),
            new FrameworkPropertyMetadata(
                3f,
                FrameworkPropertyMetadataOptions.None));

        public float CameraDistanceMultiplier
        {
            get { return (float)GetValue(CameraDistanceMultiplierProperty); }
            set { SetValue(CameraDistanceMultiplierProperty, value); }
        }
        #endregion

        protected override void CreateBuffers()
        {
            base.CreateBuffers();

            var device = this.Graphics.GraphicsDevice;

            _tempBuffer = new RenderTarget2D(
                device,
                this.TargetSize.Width,
                this.TargetSize.Height,
                1,
                device.PresentationParameters.BackBufferFormat,
                device.PresentationParameters.MultiSampleType,
                device.PresentationParameters.MultiSampleQuality);
        }

        protected override void DisposeBuffers()
        {
            base.DisposeBuffers();

            if (_tempBuffer != null)
                _tempBuffer.Dispose();

            _tempBuffer = null;
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            _projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                (float)Math.PI * 0.25f,
                (float)this.TargetSize.Width / this.TargetSize.Height,
                0.05f,
                1000f);

            var camera = new Camera(_projectionMatrix) { ViewPosition = new Vector3(-.0f, .0f, 1.2f) };
            var device = this.Graphics.GraphicsDevice;

            _sun = new Sun();
            _sun.Create(this.Graphics, this.Content, camera);

            _usePostProcessor = device.GraphicsDeviceCapabilities.DeviceType == DeviceType.Hardware &&
                                device.GraphicsDeviceCapabilities.MaxPixelShaderProfile >= ShaderProfile.PS_3_0;

            if (_usePostProcessor)
                _postProcessor = new PostProcessor(device, this.Content);
        }

        protected override void UnloadContent()
        {
            base.UnloadContent();
            
            if (_sun != null)
                _sun.Dispose();

            _sun = null;

            if (_postProcessor != null)
                _postProcessor.Dispose();

            _postProcessor = null;
        }

        protected override void Present(XnaTime time)
        {
            var milliseconds = time.ElapsedGameTime.Milliseconds;

            _dt = milliseconds / 1000.0f;
            _sun.Update(time.ElapsedGameTime);

            var device = this.Graphics.GraphicsDevice;

            if (_usePostProcessor)
            {
                device.SetRenderTarget(0, _tempBuffer);

                device.Clear(
                    options: ClearOptions.Target,
                    color: Color.TransparentBlack,
                    depth: 1.0f,
                    stencil: 0);

                _sun.Render(this.StarType);
                _postProcessor.ToneMap(_tempBuffer, this.BackBuffer, _dt, false, true);
                device.SetRenderTarget(0, this.BackBuffer);
            }
            else
            {
                device.Clear(
                    options: ClearOptions.Target,
                    color: Color.TransparentBlack,
                    depth: 1.0f,
                    stencil: 0);

                _sun.Render(this.StarType);
            }
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            drawingContext.PushClip(_clip);
            base.OnRender(drawingContext);
            drawingContext.Pop();
        }
    }
}
